/**
 * 
 */
package com.vision.core.cm.util;

import java.sql.Timestamp;
import java.util.Date;

import com.google.gwt.i18n.shared.DateTimeFormat;
import com.vision.core.cm.Constants;

/**
 * GWT-safe utility class for dealing with date and time.
 * 
 * @author Mark
 *
 */
public class DateUtils {
	
	interface IFormatter {
		String format(Date date, String pattern);
		Date parse(String value, String pattern);
	}
	
	static final IFormatter ClientFormatter = new IFormatter() {
		@Override
		public Date parse(String value, String pattern) {
			return DateTimeFormat.getFormat(pattern).parse(value);
		}
		@Override
		public String format(Date date, String pattern) {
			return DateTimeFormat.getFormat(pattern).format(date);
		}
	};
	
//	private static IFormatter ServerFormatter = null;

	public static final String BASIC_DATE_FORMAT = "MM/dd/yyyy";
	public static final String BASIC_DATE_TIME_FORMAT = "MM/dd/yyyy hh:mm a";
	public static final String DATE_WHEN_NULL = "";
	
	// Date Constants %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	public static final int ONE_HOUR_IN_MILLIS = 60 * 60 * 1000;
	
	public static final int ONE_DAY_IN_MILLIS = 24 * ONE_HOUR_IN_MILLIS;
	
	// Date Operations %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	/**
	 * {@link Date#getYear()} returns values less 1900, e.g. for year 2000, getYear() returns 100 (1900 + 100 = 2000).
	 * This method returns the year with 1900 added back. 
	 * 
	 * @param date
	 * @return
	 */
	@SuppressWarnings("deprecation")
	public static int getCalendarYear(Date date) {
		return date.getYear() + 1900;
	}
	
	/**
	 * {@link Date#getMonth()} returns values from 0 to 11 (0 being January).
	 * This method returns the month as values from 1 to 12 (1 being January). 
	 * 
	 * @param date
	 * @return
	 */
	@SuppressWarnings("deprecation")
	public static int getCalendarMonth(Date date) {
		return date.getMonth() + 1;
	}
	
	public static boolean isBefore(Date date, Date when) {
		return isBefore(date, when, true);
	}
	
	@SuppressWarnings("deprecation")
	public static boolean isBefore(Date date, Date when, boolean ignoreTime) {
		if (ignoreTime) {
			// Compare years
			int diff = date.getYear() - when.getYear();
			if (diff != 0) return diff < 0;
			
			// Years are same, compare months
			diff = date.getMonth() - when.getMonth();
			if (diff != 0) return diff < 0;
			
			// Years and months are same, compare dates
			return date.getDate() < when.getDate();
		}
		return date.before(when);
	}
	
	public static boolean isBeforeOrSame(Date date, Date when) {
		return isBeforeOrSame(date, when, true);
	}
	
	@SuppressWarnings("deprecation")
	public static boolean isBeforeOrSame(Date date, Date when, boolean ignoreTime) {
		if (ignoreTime) {
			// Compare years
			int diff = date.getYear() - when.getYear();
			if (diff != 0) return diff < 0;
			
			// Years are same, compare months
			diff = date.getMonth() - when.getMonth();
			if (diff != 0) return diff < 0;
			
			// Years and months are same, compare dates
			return date.getDate() <= when.getDate();
		}
		return date.before(when) || date.equals(when);
	}
	
	public static boolean isAfter(Date date, Date when) {
		return isAfter(date, when, true);
	}
	
	@SuppressWarnings("deprecation")
	public static boolean isAfter(Date date, Date when, boolean ignoreTime) {
		if (ignoreTime) {
			// Compare years
			int diff = date.getYear() - when.getYear();
			if (diff != 0) return diff > 0;
			
			// Years are same, compare months
			diff = date.getMonth() - when.getMonth();
			if (diff != 0) return diff > 0;
			
			// Years and months are same, compare dates
			return date.getDate() > when.getDate();
		}
		return date.after(when);
	}
	
	public static boolean isAfterOrSame(Date date, Date when) {
		return isAfterOrSame(date, when, true);
	}
	
	@SuppressWarnings("deprecation")
	public static boolean isAfterOrSame(Date date, Date when, boolean ignoreTime) {
		if (ignoreTime) {
			// Compare years
			int diff = date.getYear() - when.getYear();
			if (diff != 0) return diff > 0;
			
			// Years are same, compare months
			diff = date.getMonth() - when.getMonth();
			if (diff != 0) return diff > 0;
			
			// Years and months are same, compare dates
			return date.getDate() >= when.getDate();
		}
		return date.after(when) || date.equals(when);
	}
	
	// Date Formatting %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

	public static String getBasicDateFormatted(Date date) {
		if (date == null) return DATE_WHEN_NULL;
		return getFormatter().format(date, BASIC_DATE_FORMAT);
//		return DateTimeFormat.getFormat(BASIC_DATE_FORMAT).format(date);
	}
	
	public static String getBasicDateTimeFormatted(Date date) {
		if (date == null) return DATE_WHEN_NULL;
		return getFormatter().format(date, BASIC_DATE_TIME_FORMAT);
//		return DateTimeFormat.getFormat(BASIC_DATE_TIME_FORMAT).format(date);
	}
	
	public static String getUserFriendlyDate(Date date) {
		if (date == null) return DATE_WHEN_NULL;
		return DateUtils.getUserFriendlyDate(date, DATE_WHEN_NULL, "MMM d, yyyy hh:mm a");
	}

	/**
	 * 
	 * @param date
	 * @param whenNull
	 * @param dateFormat
	 * @return
	 */
	public static String getUserFriendlyDate(Date date, String whenNull, String dateFormat) {
		if (date == null)
			return whenNull;
		
		Timestamp now = new Timestamp(System.currentTimeMillis());
		long diff = now.getTime() - date.getTime();
		
		// Within the hour
		if (diff < Constants.HOUR_IN_MILLIS) {
			long mins = (long) (diff / 1000d / 60d); 
			return mins + (mins > 1 ? " mins" : " min") + " ago";
		}
		
		// Within the day
		if (diff < Constants.DAY_IN_MILLIS) {
			long hours = (long) (diff / 1000d / 60d / 60d);
			long mins = (long) (diff / 1000d / 60d % 60d);
			return hours + (hours > 1 ? " hrs" : " hr") + ( mins > 0 ? (" " + mins + (mins > 1 ? " mins" : " min")) : "" ) + " ago";
		}
		
		if (diff < Constants.YEAR_IN_MILLIS) {
			// TODO 9 Implement
		}
		
		return getFormatter().format(date, dateFormat);
	}
	
	private static IFormatter getFormatter() {
		return ClientFormatter;
	}
	
//	private static IFormatter getServerFormatterInstance() {
//		if (ServerFormatter == null)
//			ServerFormatter = new IFormatter() {
//				@Override
//				public Date parse(String value, String pattern) {
//					try {
//						return new SimpleDateFormat(pattern).parse(value);
//					} catch (Exception e) {
//						e.printStackTrace(); // TODO Log properly
//						return null;
//					}
//				}
//				@Override
//				public String format(Date date, String pattern) {
//					return new SimpleDateFormat(pattern).format(date);
//				}
//			};
//		return ServerFormatter;
//	}

}
